/************************************************************
 * flash partition management with BOOTLOADER
 ************************************************************/
#include "gkt_board.h"
#include "gkt_flash.h"
#include "gkt_flash_partition.h"
#include "string.h"
#include "gkt_crc16.h"
#include "gkt_debug.h"
#include "gkt_driver.h"

#if defined(GKT_CONFIG_BOOTLOADER_SUPPORT) && GKT_CONFIG_BOOTLOADER_SUPPORT

#ifndef GKT_CONFIG_PARTITION_TABLE_MAXSIZE
#error "not define GKT_CONFIG_PARTITION_TABLE_MAXSIZE."
#endif

#ifndef GKT_CONFIG_PARTITION_TABLE_ARRD
#error "not define GKT_CONFIG_PARTITION_TABLE_ARRD."
#endif

static const flash_partition_table_s *sc_flash_partition_table_bs;
const gkt_flash_partition_magic_desc_s sc_flash_partition_magic[] = {
	GKT_CONFIG_FLASH_PARTITION_MAGIC_TABLE
};

#ifdef GKT_CONFIG_PARTITION_MAX_SIZE
#define PARTITION_READ_SIZE	GKT_CONFIG_PARTITION_MAX_SIZE
#else
#define PARTITION_READ_SIZE	0x1000
#endif
static uint8_t partition_read_buf[PARTITION_READ_SIZE];

uint32_t gkt_flash_partition_id(const char *name, uint32_t *mid)
{
	const gkt_flash_partition_desc_s *part_desc;
	uint32_t i, sub_nums;

	sub_nums = 0;
	if (name && sc_flash_partition_table_bs) {
		for (i = 0; i < FLASH_PARTITION_MAXNUMS; i++) {
			part_desc = &(sc_flash_partition_table_bs->descs[i]);
			if (part_desc->name && !strcmp(part_desc->name, name)) {
				if (!sub_nums && mid)
					*mid = part_desc->current.mid;
				sub_nums++;
			}
		}
	}

	return sub_nums;
}

static const gkt_flash_partition_desc_s *
	flash_partition_desc(uint32_t cid)
{
	const gkt_flash_partition_desc_s *part_desc;
	uint32_t i;
	
	if ((cid < GKT_FLASH_PARTITION_CID_MAX) && sc_flash_partition_table_bs) {
		for (i = 0; i < FLASH_PARTITION_MAXNUMS; i++) {
			part_desc = &(sc_flash_partition_table_bs->descs[i]);
			if ((uint32_t)part_desc->current.cid == cid)
				return part_desc;
		}
	}

	gkt_error("flash_partition_desc: cid(0x%x) - no matched desc.\n", cid);
	return NULL;
}

const gkt_flash_partition_desc_s *
	gkt_flash_partition_desc(uint32_t cid)
{
	return flash_partition_desc(cid);
}

int gkt_flash_partition_is_valid(uint32_t cid)
{
	return 0;
}

uint32_t gkt_flash_partition_get_table_offset(uint32_t item,uint32_t cid,uint32_t *p_offset)
{
	uint32_t offset,i,index;
	const gkt_flash_partition_desc_s *part_desc;
	uint32_t retval = GKT_SUCCESS;

	offset = ~0;
	if(sc_flash_partition_table_bs == NULL){
		retval = GKT_ENOTEXIST;
	}
	else{
		switch(item){
			case	GKT_TABLE_IETM_HEADER:
				offset = 0;
			break;
			case	GKT_TABLE_IETM_DESC:
				index = 0;
				offset = GKT_FLASH_PARTITION_HEADER_SIZE;
				for (i = 0; i < FLASH_PARTITION_MAXNUMS; i++) {
					part_desc = &(sc_flash_partition_table_bs->descs[i]);
					if (part_desc->current.cid != cid) {
						index++;
					}
					else{
						break;
					}
				}
				if(index < FLASH_PARTITION_MAXNUMS)
					offset += index*sizeof(gkt_flash_partition_desc_s);
				else{
					retval = GKT_ENOTEXIST;
					offset = ~0;
				}
			break;
			case	GKT_TABLE_IETM_MOVE:
				offset = GKT_FLASH_PARTITION_HEADER_SIZE;
				offset += FLASH_PARTITION_MAXNUMS*sizeof(gkt_flash_partition_desc_s);
			break;
			case	GKT_TABLE_IETM_SHARE_FLAG:
				offset = (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1) - GKT_FLASH_PARTITION_TAIL_SIZE;
				offset -= sizeof(flash_share_flag_s);
			break;
			case	GKT_TABLE_IETM_TAIL:
				offset = (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1) - GKT_FLASH_PARTITION_TAIL_SIZE;
			break;
			default:
				retval = GKT_ENOTEXIST;
				offset = ~0;
			break;
		}
	}
	if(p_offset)
		*p_offset = offset;
	else{
		retval = GKT_ENOTEXIST;
	}

	return retval;
}

uint32_t gkt_flash_partition_get_valid_item_offset(uint32_t *p_offset)
{
	uint32_t offset,i,index;
	const gkt_flash_partition_desc_s *part_desc;
	uint32_t retval = GKT_SUCCESS;

	index = 0;
	offset = GKT_FLASH_PARTITION_HEADER_SIZE;
	for (i = 0; i < FLASH_PARTITION_MAXNUMS; i++) {
		part_desc = &(sc_flash_partition_table_bs->descs[i]);
		if ((part_desc->current.mid < GKT_FLASH_PARTITION_MID_MAX) \
				&&(part_desc->current.mid > GKT_FLASH_PARTITION_MID_UNUSED)	\
				&&(part_desc->type < GKT_FLASH_PARTITION_TYPE_MAX)) {
			index++;
		}
		else{
			break;
		}
	}
	if(index < FLASH_PARTITION_MAXNUMS){
		offset += index*sizeof(gkt_flash_partition_desc_s);
	}
	else{
		retval = GKT_ENOTEXIST;
		offset = ~0;
	}
	if(p_offset)
		*p_offset = offset;
	else{
		retval = GKT_ENOTEXIST;
	}
	
	return retval;
}

static int flash_partition_real_data_init(const gkt_flash_partition_desc_s *desc)
{
	gkt_flash_partition_header_s *c_header;
	gkt_flash_partition_tail_s *c_tail;
	int retval;
	uint32_t half_size,valid_index;
	uint16_t crc16;

	if((!desc) || (desc->device_id != GKT_FLASH_PARTITION_DEVICE_ID_EFLASH)) {
		return GKT_EPARAM;
	}

	half_size = (desc->total_size_KB)<<10;
	half_size = half_size>>1;
	if(half_size > PARTITION_READ_SIZE){
		return GKT_EPARAM;
	}
	memset(partition_read_buf,0xff,sizeof(half_size));
	c_header = (gkt_flash_partition_header_s *)partition_read_buf;
	c_header->magic = GKT_PARTITION_TABLE_GET_MAGIC(desc->current.mid);
	c_header->sequence = 1;
	c_tail = (gkt_flash_partition_tail_s *)
					(partition_read_buf + half_size - GKT_FLASH_PARTITION_TAIL_SIZE);
	c_tail->magic = GKT_PARTITION_TABLE_GET_MAGIC(desc->current.mid);
	crc16 = gkt_crc16_ccitt_false(partition_read_buf, half_size - 2,NULL);
	c_tail->crc16 = crc16;

	valid_index = 0;
	retval = gkt_flash_erase(desc->device_id,
				GKT_CONFIG_PARTITION_TABLE_OFFSET + half_size*valid_index, half_size);
	if(retval == GKT_SUCCESS){
		retval = gkt_flash_write(desc->device_id, 
					GKT_CONFIG_PARTITION_TABLE_OFFSET + half_size*valid_index, half_size, partition_read_buf);
		if(retval == GKT_SUCCESS){
			valid_index = (valid_index)?0:1;
			gkt_flash_erase(desc->device_id, 
				GKT_CONFIG_PARTITION_TABLE_OFFSET + half_size*valid_index, half_size);
			gkt_flash_write(desc->device_id, 
				GKT_CONFIG_PARTITION_TABLE_OFFSET + half_size*valid_index, half_size, partition_read_buf);
		}
	}

	return retval;
}

/******************************
 * ping-pong layout just support in eflash
 * |--------------------|
 * |  partition_header  |
 * |  <private data>    |
 * |  partition_tail    |
 * |--------------------|
 ******************************/
static uint32_t flash_partition_pp_init(const gkt_flash_partition_desc_s *desc,uint32_t *valid_index)
{
	const gkt_flash_partition_header_s *c_header[2];
	const gkt_flash_partition_tail_s *c_tail[2];
	const uint8_t *c_read_map_addr;
	int retval;
	uint32_t i,half_size,temp_index;
	uint16_t crc16;

	if((!desc) || (!valid_index) || (desc->current.mid > GKT_FLASH_PARTITION_MID_MAX)){
		return GKT_EPARAM;
	}

	half_size = (desc->total_size_KB)<<10;
	half_size = half_size>>1;
	temp_index = ~0;
	c_read_map_addr = (const uint8_t *)gkt_flash_map((desc->device_id),0,0);
	c_read_map_addr += desc->offset_KB<<10;
	if (desc->device_id == GKT_FLASH_PARTITION_DEVICE_ID_EFLASH) {
		for (i = 0; i < 2; i++) {
			c_read_map_addr += half_size * i;
			c_header[i] = (const gkt_flash_partition_header_s *)c_read_map_addr;
			if (c_header[i]->magic != sc_flash_partition_magic[desc->current.mid].magic)
				continue;
	
			c_tail[i] = (const gkt_flash_partition_tail_s *)
				(c_read_map_addr + half_size - GKT_FLASH_PARTITION_TAIL_SIZE);
			if (c_tail[i]->magic != sc_flash_partition_magic[desc->current.mid].magic)
				continue;

			crc16 = gkt_crc16_ccitt_false(c_read_map_addr, half_size - 2,NULL);
			if (crc16 != c_tail[i]->crc16)
				continue;

			if ((temp_index > 1)	\
			|| ((c_header[temp_index]->sequence < c_header[i]->sequence)&&(c_header[temp_index]->sequence != 0))	\
			|| (c_header[i]->sequence == 0))
			{
				temp_index = i;
			}
		}

		if(temp_index > 1){
			retval = flash_partition_real_data_init(desc);
			if(retval == GKT_SUCCESS)
				temp_index = 0;
		}
		*valid_index = temp_index;
		gkt_trace("flash_partition_pp_init: name(%s), pp_active_index(%u)\n", 
			desc->name, desc->pp_active_index);
		return GKT_SUCCESS;
	}

	return GKT_ENOTSUPPORT;//spiflash not support
}

static int flash_partition_pp_read(const gkt_flash_partition_desc_s *desc,
				void *data, uint32_t offset, uint32_t size)
{
	uint32_t valid_index;
	uint32_t half_size;
	int retval;

	retval = flash_partition_pp_init(desc,&valid_index);
	if (retval != GKT_SUCCESS)
		return retval;
	else if (valid_index > 1)
		return GKT_ENOTINIT;

	// TODO:
	half_size = (desc->total_size_KB)<<10;
	half_size = half_size>>1;
	if ((offset < half_size) 
		&& (size <= half_size)
		&& (half_size >= (offset + size)))
	{
		retval = gkt_flash_read(desc->device_id, 
					(desc->offset_KB<<10) + offset + half_size*valid_index, size, data);
	}
	else
		retval = GKT_EOVERFLOW;
	return retval;
}

static int flash_partition_pp_write(const gkt_flash_partition_desc_s *desc,
				const void *data, uint32_t offset, uint32_t size)
{
	int retval;
	uint32_t half_size;
	uint32_t valid_index;
	gkt_flash_partition_header_s *c_header;
	uint16_t crc16;

	retval = flash_partition_pp_init(desc,&valid_index);
	if (retval != GKT_SUCCESS)
		return retval;
	else if (valid_index > 1)
		return GKT_ENOTINIT;//not init

	// TODO:
	if (desc->flags & GKT_FLASH_PARTITION_F_WRITABLE) {
		half_size = (desc->total_size_KB)<<10;
		half_size = half_size>>1;	
		if ((offset < half_size) 
			&& (size <= half_size)
			&& (half_size >= (offset + size)))
		{
			retval = gkt_flash_read(desc->device_id, 
						(desc->offset_KB<<10) + half_size*valid_index, half_size, partition_read_buf);
			if(retval != GKT_SUCCESS)
				return retval;
			else
				memcpy(partition_read_buf+offset,(uint8_t *)data,size);
			
			c_header = (gkt_flash_partition_header_s *)partition_read_buf;
			c_header->sequence++;
			crc16 = gkt_crc16_ccitt_false(partition_read_buf, half_size - 2,NULL);
			memcpy(partition_read_buf+half_size-2, (uint8_t *)(&crc16),2);

			valid_index = (valid_index)?0:1;
			retval = gkt_flash_erase(desc->device_id, 
										((desc->offset_KB<<10) + half_size*valid_index), half_size);
			if(retval == GKT_SUCCESS)
				retval = gkt_flash_write(desc->device_id, 
							(desc->offset_KB<<10) + half_size*valid_index, half_size, partition_read_buf);
		}
		else
			retval = GKT_EOVERFLOW;
	}
	return retval;
}

/******************************
 * ms layout just support in eflash
 * |--------------------|
 * |  partition_header	|
 * |  <private data>	|
 * |  partition_tail	|
 * |--------------------|
 * |  partition_header	|
 * |  <private data>	|
 * |  partition_tail	|
 ******************************/
static uint32_t flash_partition_ms_init(const gkt_flash_partition_desc_s *desc,uint32_t *valid_index)
{
	const gkt_flash_partition_header_s *c_header[2];
	const gkt_flash_partition_tail_s *c_tail[2];
	const uint8_t *c_read_map_addr;
	int retval;
	uint32_t i,half_size,temp_index;
	uint16_t crc16;

	if((!desc) || (!valid_index)  || (desc->current.mid > GKT_FLASH_PARTITION_MID_MAX)){
		return GKT_EPARAM;
	}

	half_size = (desc->total_size_KB)<<10;
	half_size = half_size>>1;

	temp_index = ~0;
	c_read_map_addr = (const uint8_t *)gkt_flash_map((desc->device_id),0,0);
	c_read_map_addr += desc->offset_KB<<10;
	if (desc->device_id == GKT_FLASH_PARTITION_DEVICE_ID_EFLASH) {
		for (i = 0; i < 2; i++) {
			c_read_map_addr += half_size * i;
			c_header[i] = (const gkt_flash_partition_header_s *)c_read_map_addr;
			if (c_header[i]->magic != sc_flash_partition_magic[desc->current.mid].magic)
				continue;
	
			c_tail[i] = (const gkt_flash_partition_tail_s *)
				(c_read_map_addr + half_size - GKT_FLASH_PARTITION_TAIL_SIZE);
			if (c_tail[i]->magic != sc_flash_partition_magic[desc->current.mid].magic)
				continue;

			crc16 = gkt_crc16_ccitt_false(c_read_map_addr, half_size - 2,NULL);
			if (crc16 != c_tail[i]->crc16)
				continue;

			if ((temp_index > 1)	\
			|| ((c_header[temp_index]->sequence < c_header[i]->sequence)&&(c_header[temp_index]->sequence != 0))	\
			|| (c_header[i]->sequence == 0))
			{
				temp_index = i;
			}
		}

		if(temp_index > 1){
			retval = flash_partition_real_data_init(desc);
			if(retval == GKT_SUCCESS)
				temp_index = 0;
		}
		*valid_index = temp_index;
		gkt_trace("flash_partition_pp_init: name(%s), pp_active_index(%u)\n", 
			desc->name, desc->pp_active_index);
		return GKT_SUCCESS;
	}

	return GKT_ENOTSUPPORT;//spiflash not support
}

static int flash_partition_ms_read(const gkt_flash_partition_desc_s *desc,
				void *data, uint32_t offset, uint32_t size)
{
	uint32_t valid_index;
	uint32_t half_size;
	int retval;

	retval = flash_partition_ms_init(desc,&valid_index);
	if (retval != GKT_SUCCESS)
		return retval;
	else if (valid_index > 1)
		return GKT_ENOTINIT;

	// TODO:
	half_size = (desc->total_size_KB)<<10;
	half_size = half_size>>1;

	if ((offset < half_size) 
		&& (size <= half_size)
		&& (half_size >= (offset + size)))
	{
		retval = gkt_flash_read(desc->device_id, 
					(desc->offset_KB<<10) + offset + half_size*valid_index, size, data);
	}
	else
		retval = GKT_EOVERFLOW;
	return retval;
}

static int flash_partition_ms_write(const gkt_flash_partition_desc_s *desc,
				const void *data, uint32_t offset, uint32_t size)
{
	int retval;
	uint32_t half_size;
	uint32_t valid_index;
	gkt_flash_partition_header_s *c_header;
	uint16_t crc16;

gkt_printf("offset =%x,data=%s,size=%x,device_id=%d\n",offset,desc->name,size,desc->device_id);
	retval = flash_partition_ms_init(desc,&valid_index);
	if (retval != GKT_SUCCESS)
		return retval;
	else if (valid_index > 1)
		return GKT_ENOTINIT;//not init
gkt_printf("validindex = %d\n",valid_index);
	// TODO:
	if (desc->flags & GKT_FLASH_PARTITION_F_WRITABLE) {
		half_size = (desc->total_size_KB)<<10;
		half_size = half_size>>1;

		if ((offset < half_size) 
			&& (size <= half_size)
			&& (half_size >= (offset + size)))
		{
			retval = gkt_flash_read(desc->device_id, 
						(desc->offset_KB<<10) + half_size*valid_index, half_size, partition_read_buf);
			if(retval != GKT_SUCCESS)
				return retval;
			else
				memcpy(partition_read_buf+offset,(uint8_t *)data,size);

			c_header = (gkt_flash_partition_header_s *)partition_read_buf;
			c_header->sequence++;
			crc16 = gkt_crc16_ccitt_false(partition_read_buf, half_size - 2,NULL);
			memcpy(partition_read_buf+half_size-2, (uint8_t *)(&crc16),2);

			valid_index = (valid_index)?0:1;
			retval = gkt_flash_erase(desc->device_id, 
										((desc->offset_KB<<10) + half_size*valid_index), half_size);
			if(retval == GKT_SUCCESS){
				retval = gkt_flash_write(desc->device_id, 
							(desc->offset_KB<<10) + half_size*valid_index, half_size, partition_read_buf);
				if(retval == GKT_SUCCESS){
					if(desc->current.mid == GKT_FLASH_PARTITION_MID_TABLE){
						sc_flash_partition_table_bs = (flash_partition_table_s *)(GKT_CONFIG_PARTITION_TABLE_ARRD	\
														 + (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1)*valid_index);
					}
					valid_index = (valid_index)?0:1;
					gkt_flash_erase(desc->device_id, 
										((desc->offset_KB<<10) + half_size*valid_index), half_size);
					retval = gkt_flash_write(desc->device_id, 
							(desc->offset_KB<<10) + half_size*valid_index, half_size, partition_read_buf);
					if((retval == GKT_SUCCESS) && (desc->current.mid == GKT_FLASH_PARTITION_MID_TABLE)){
						sc_flash_partition_table_bs = (flash_partition_table_s *)(GKT_CONFIG_PARTITION_TABLE_ARRD	\
																			 + (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1)*valid_index);
					}
					retval = GKT_SUCCESS;
				}
			}
		}
		else
			retval = GKT_EOVERFLOW;
	}
	return retval;
}

int gkt_flash_partition_read(uint32_t cid, 
				void *data, uint32_t offset, uint32_t size)
{
	const gkt_flash_partition_desc_s *part_desc;
	uint32_t total_size;
	int retval;

	if ((!data) || (!size))
		return GKT_EPARAM;

	part_desc = flash_partition_desc(cid);
	if (part_desc) {
		if (GKT_FLASH_PARTITION_MODE_PING_PONG == part_desc->mode)
			return flash_partition_pp_read(part_desc, data, offset, size);
		else if (GKT_FLASH_PARTITION_MODE_MS_BACKUP == part_desc->mode){
			return flash_partition_ms_read(part_desc, data, offset, size);
		}

		total_size = part_desc->total_size_KB<<10;
		if ((offset < total_size) 
			&& (size <= total_size)
			&& ((offset+size) <= total_size))
		{
			if(part_desc->device_id == GKT_FLASH_PARTITION_DEVICE_ID_SPIFLASH)
				gkt_printf("spi offset = (0x%x),size=(0x%x)\n",((part_desc->offset_KB<<10) + offset),size);
			retval = gkt_flash_read(part_desc->device_id, 
						((part_desc->offset_KB<<10) + offset), size, data);
		}
		else
			retval = GKT_EOVERFLOW;
	}
	else
		retval = GKT_ENOTEXIST;

	return retval;
}

int gkt_flash_partition_write(uint32_t cid, 
				const void *data, uint32_t offset, uint32_t size)
{
	const gkt_flash_partition_desc_s *part_desc;
	uint32_t total_size;
	int retval;

	if ((!data) || (!size))
		return GKT_EPARAM;

	part_desc = flash_partition_desc(cid);
	if (part_desc) {
		gkt_trace("flash_partition_write: name(%s), flags(0x%x), range(0x%x-0x%x)\n",
			part_desc->name, part_desc->flags, offset, size);

		if (GKT_FLASH_PARTITION_MODE_PING_PONG == part_desc->mode)
			return flash_partition_pp_write(part_desc, data, offset, size);
		else if (GKT_FLASH_PARTITION_MODE_MS_BACKUP == part_desc->mode){
			return flash_partition_ms_write(part_desc, data, offset, size);
		}

		if (part_desc->flags & GKT_FLASH_PARTITION_F_WRITABLE) {
			total_size = part_desc->total_size_KB<<10;
			if ((offset < total_size) 
				&& (size <= total_size)
				&& ((offset+size) <= total_size))
			{
				retval = gkt_flash_write(part_desc->device_id, 
							((part_desc->offset_KB<<10) + offset), size, data);
			}
			else
				retval = GKT_EOVERFLOW;
		}
		else
			retval = GKT_EPERMIT;
	}
	else
		retval = GKT_ENOTEXIST;

	return retval;
}

int gkt_flash_partition_table_write(uint32_t dev_id, 
				const void *data, uint32_t offset, uint32_t size)
{
	int retval;
	gkt_flash_partition_header_s *c_header;
	uint32_t offset_table,valid_index,half_size;
	uint16_t crc16;

	if(dev_id != GKT_FLASH_PARTITION_DEVICE_ID_EFLASH){
		return GKT_ENOTSUPPORT;
	}

	if(sc_flash_partition_table_bs == NULL){
		offset_table = GKT_CONFIG_PARTITION_TABLE_OFFSET;
	}
	else{
		offset_table = (uint32_t)sc_flash_partition_table_bs;
		offset_table -= EFLASH_ADDR_START;
	}
	half_size = GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1;
	retval = gkt_flash_read(dev_id, 
				offset_table, half_size, partition_read_buf);
	if(retval != GKT_SUCCESS){
		return retval;
	}
	if(offset > half_size)
		return GKT_EPARAM;

	memcpy(partition_read_buf+offset,(uint8_t *)data,size);
	c_header = (gkt_flash_partition_header_s *)partition_read_buf;
	c_header->sequence++;
	crc16 = gkt_crc16_ccitt_false(partition_read_buf, half_size - 2,NULL);
	memcpy(partition_read_buf+half_size-2, (uint8_t *)(&crc16),2);
	if(offset_table == GKT_CONFIG_PARTITION_TABLE_OFFSET){
		valid_index = 0;
	}
	else{
		valid_index = 1;
	}

	valid_index = (valid_index)?0:1;
	retval = gkt_flash_erase(dev_id,
				GKT_CONFIG_PARTITION_TABLE_OFFSET + half_size*valid_index, half_size);
	if(retval == GKT_SUCCESS){
		retval = gkt_flash_write(dev_id, 
					GKT_CONFIG_PARTITION_TABLE_OFFSET + half_size*valid_index, half_size, partition_read_buf);
		if(retval == GKT_SUCCESS){
			sc_flash_partition_table_bs = (flash_partition_table_s *)(GKT_CONFIG_PARTITION_TABLE_ARRD	\
											 + (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1)*valid_index);
			valid_index = (valid_index)?0:1;
			gkt_flash_erase(dev_id, 
				GKT_CONFIG_PARTITION_TABLE_OFFSET + half_size*valid_index, half_size);
			gkt_flash_write(dev_id, 
				GKT_CONFIG_PARTITION_TABLE_OFFSET + half_size*valid_index, half_size, partition_read_buf);
			if(retval == GKT_SUCCESS){
				sc_flash_partition_table_bs = (flash_partition_table_s *)(GKT_CONFIG_PARTITION_TABLE_ARRD	\
																	 + (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1)*valid_index);
			}
			retval = GKT_SUCCESS;
		}
	}

	return retval;
}

int gkt_flash_partition_erase(uint32_t cid, uint32_t offset, 
				uint32_t size)
{
	const gkt_flash_partition_desc_s *part_desc;
	uint32_t erase_size;
	int retval;

	if (!size)
		return GKT_EPARAM;

	part_desc = flash_partition_desc(cid);
	if (part_desc) {
		gkt_trace("flash_partition_erase: name(%s), flags(0x%x), range(0x%x-0x%x)\n",
			part_desc->name, part_desc->flags, offset, size);

		if (part_desc->flags & GKT_FLASH_PARTITION_F_WRITABLE) {
			erase_size = part_desc->total_size_KB<<10;
			if (offset < erase_size) {
				erase_size = erase_size - offset;
				if (size < erase_size)
					erase_size = size;

				retval = gkt_flash_erase(part_desc->device_id, 
							((part_desc->offset_KB<<10) + offset), erase_size);
			}
			else{
				retval = GKT_EOVERFLOW;
			}
		}
		else{
			retval = GKT_EPERMIT;
		}
	}
	else{
		retval = GKT_ENOTEXIST;
	}

	return retval;
}

const void *gkt_flash_partition_map_addr(uint32_t cid)
{
	const gkt_flash_partition_desc_s *part_desc;
	uint32_t retval,valid_index;
	const uint8_t *addr;

	retval = GKT_ENORESOURCE;
	part_desc = flash_partition_desc(cid);
	if (part_desc) {
		if (GKT_FLASH_PARTITION_MODE_PING_PONG == part_desc->mode){
			retval = flash_partition_pp_init(part_desc,&valid_index);
			if((retval == GKT_SUCCESS) && (valid_index < 2)){
				addr = gkt_flash_map(part_desc->device_id,0,0) + part_desc->offset_KB*(0x400)	\
						+ valid_index*(part_desc->total_size_KB*(0X400)/2);
			}
			else{
				retval = GKT_ENORESOURCE;
			}
		}
		else if (GKT_FLASH_PARTITION_MODE_MS_BACKUP == part_desc->mode){
			retval = flash_partition_ms_init(part_desc,&valid_index);
			if((retval == GKT_SUCCESS) && (valid_index < 2)){
				addr = gkt_flash_map(part_desc->device_id,0,0) + part_desc->offset_KB*(0x400)	\
						+ valid_index*(part_desc->total_size_KB*(0X400)/2);
			}
			else{
				retval = GKT_ENORESOURCE;
			}
		}
		else {
			if(part_desc->device_id == GKT_FLASH_PARTITION_DEVICE_ID_EFLASH){
				retval = GKT_SUCCESS;
				addr = gkt_flash_map(part_desc->device_id,0,0) + part_desc->offset_KB*(0x400);
			}
			else{
				retval = GKT_ENORESOURCE;
			}
		}
	}

	if(retval == GKT_SUCCESS){
		return (const void *)addr;
	}
	else{
		return NULL;
	}
}

int gkt_flash_partition_set_attr(uint32_t cid,uint32_t attr_type,uint32_t attr_value)
{
	gkt_flash_partition_desc_s part_desc;
	const gkt_flash_partition_desc_s *p_part_desc;
	uint32_t offset,result;
	int retval = GKT_EPARAM;

	p_part_desc = flash_partition_desc(cid);
	if (p_part_desc) {
		result = gkt_flash_partition_get_table_offset(GKT_TABLE_IETM_DESC,cid,&offset);
		if((result == GKT_SUCCESS) && (offset < GKT_CONFIG_PARTITION_TABLE_MAXSIZE)){
			memcpy((uint8_t *)(&part_desc),(uint8_t *)p_part_desc,sizeof(gkt_flash_partition_desc_s));
			switch(attr_type){
				case	GKT_FLASH_PARTITION_SET_WRITABLE:
						if(attr_value <= GKT_FLASH_PARTITION_F_WRITABLE){
							part_desc.flags &= ~(1<<2);
							part_desc.flags |= attr_value;
							retval = GKT_SUCCESS;
						}						
				break;
				case	GKT_FLASH_PARTITION_SET_TYPE:
						if(attr_value < GKT_FLASH_PARTITION_TYPE_MAX){
							part_desc.type = attr_value;
							retval = GKT_SUCCESS;
						}
				break;
				case	GKT_FLASH_PARTITION_SET_ACTUAL_SIZE:
						part_desc.real_size = attr_value;
						retval = GKT_SUCCESS;
				break;
				case	GKT_FLASH_PARTITION_SET_UNIT_SIZE:
						part_desc.division.unit_size = attr_value;
						retval = GKT_SUCCESS;
				break;
				default:

				break;
			}
		}

		if(retval == GKT_SUCCESS){
			retval = gkt_flash_partition_table_write(GKT_FLASH_PARTITION_DEVICE_ID_EFLASH,&part_desc,offset,sizeof(gkt_flash_partition_desc_s));
		}
	}

	return retval;
}


int gkt_flash_partition_associate(uint32_t source_cid,uint32_t target_cid)
{
	gkt_flash_partition_desc_s part_desc;
	const gkt_flash_partition_desc_s *p_part_desc;
	int retval = GKT_EPARAM;
	uint32_t result,offset;

	p_part_desc = flash_partition_desc(source_cid);
	if (p_part_desc) {
		if(p_part_desc->type != GKT_FLASH_PARTITION_TYPE_ACTIVE){
			result = gkt_flash_partition_get_table_offset(GKT_TABLE_IETM_DESC,source_cid,&offset);
			if((result == GKT_SUCCESS) && (offset < GKT_CONFIG_PARTITION_TABLE_MAXSIZE)){
				memcpy((uint8_t *)(&part_desc),(uint8_t *)p_part_desc,sizeof(gkt_flash_partition_desc_s));
				part_desc.associate.cid = target_cid;
				retval = gkt_flash_partition_write(GKT_PARTITION_TABLE_CID,&part_desc,offset,sizeof(gkt_flash_partition_desc_s));	
			}
		}
	}

	return retval;
}

int gkt_flash_partition_resize(uint32_t cid,uint32_t new_size)
{
	const gkt_flash_partition_desc_s *part_desc;
	int retval = GKT_EPARAM;

	part_desc = flash_partition_desc(cid);
	if (part_desc) {

	}

	return retval;
}

void gkt_flash_partition_set_real_size(uint32_t cid, 
			uint32_t real_size)
{
	gkt_unused(cid);
	gkt_unused(real_size);
}

int gkt_flash_partition_init(void)
{
	const gkt_flash_partition_header_s *c_header[2];
	const gkt_flash_partition_tail_s *c_tail[2];
	const uint8_t *c_read_map_addr;
	uint32_t i,valid_index;
	uint16_t crc16;

	sc_flash_partition_table_bs = NULL;
	valid_index = ~0;
	c_read_map_addr = (uint8_t *)GKT_CONFIG_PARTITION_TABLE_ARRD;
	for (i = 0; i < 2; i++) {
		c_read_map_addr += (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1)*i;
		c_header[i] = (const gkt_flash_partition_header_s *)(c_read_map_addr);
		if (c_header[i]->magic != sc_flash_partition_magic[GKT_FLASH_PARTITION_MID_TABLE].magic){
			gkt_printf("head magic %x\n",c_header[i]->magic);
			continue;
		}
		gkt_info("p_head ok\n");
		c_tail[i] = (const gkt_flash_partition_tail_s *)	\
				(c_read_map_addr + (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1) - GKT_FLASH_PARTITION_TAIL_SIZE);
		if (c_tail[i]->magic != sc_flash_partition_magic[GKT_FLASH_PARTITION_MID_TABLE].magic)
			continue;

		gkt_info("p_tail ok\n");

		crc16 = gkt_crc16_ccitt_false(c_read_map_addr, (GKT_CONFIG_PARTITION_TABLE_MAXSIZE>>1) - 2,NULL);
		if (crc16 != c_tail[i]->crc16)
			continue;
		gkt_info("p_crc ok\n");

		if ((valid_index > 1)	\
			|| ((c_header[valid_index]->sequence < c_header[i]->sequence)&&(c_header[valid_index]->sequence != 0))	\
			|| (c_header[i]->sequence == 0))// 0xffffffff+1 = 0
		{
			if(valid_index > 1)
				gkt_info("valid_index = (%x)\n",valid_index);
			else{
				gkt_info("valid_index = (%x),i=(%x)\n",c_header[valid_index]->sequence,c_header[i]->sequence);
			}
			valid_index = i;
			sc_flash_partition_table_bs = (const flash_partition_table_s *)c_read_map_addr;
		}
	}
	gkt_printf("partition valid_index = %x\n",valid_index);
	if(valid_index > 1){
		gkt_error("flash_partition_init: data is wrong\n");
		return GKT_ENOTSUPPORT;
	}
	else{
		return GKT_SUCCESS;
	}
}

GKT_DEVICE_DESC_DEFINE_2(flash_partition,NORMAL,eflash,spi);
#endif

